package com.symaster.common.geom.show.map.create;

import com.symaster.common.geom.entity.LineSegment;
import com.symaster.common.geom.entity.Map;
import com.symaster.common.geom.entity.Point;
import com.symaster.common.geom.entity.Polygon;
import com.symaster.common.geom.show.map.create.button.CreateCurveButton;
import com.symaster.common.geom.show.map.create.button.CreatePathButton;
import com.symaster.common.geom.show.map.create.button.CreateStraightLineButton;
import com.symaster.common.swing.FpsManager;
import com.symaster.common.swing.util.SwingUtil;

import javax.swing.*;
import java.awt.*;
import java.awt.geom.Rectangle2D;
import java.util.HashSet;
import java.util.Set;

/**
 * @author yinmiao
 * @version 2023/11/14
 */
public class MapCreateView extends JComponent {

    private final Set<Polygon> polygons;
    private final Set<LineSegment> lineSegments;
    private final Set<Point> controllerPoints;
    private final EditController editController;
    private final FpsManager fpsManager;

    private JToolBar jToolBar;

    public String message;

    public MapCreateView() {
        this.polygons = new HashSet<>();
        this.lineSegments = new HashSet<>();
        this.controllerPoints = new HashSet<>();
        this.editController = new EditController(this);
        this.fpsManager = new FpsManager();
        initListener();
    }

    public void putLineSegment(LineSegment lineSegment) {
        lineSegments.add(lineSegment);
    }

    public void putControllerPoint(Point point) {
        controllerPoints.add(point);
    }

    public void putPolygon(Polygon polygon) {
        polygons.add(polygon);
    }

    public void removeLineSegment(LineSegment lineSegment) {
        lineSegments.remove(lineSegment);
    }

    public void removeControllerPoint(Point point) {
        controllerPoints.remove(point);
    }

    public void removePolygon(Polygon polygon) {
        polygons.remove(polygon);
    }

    public Set<Polygon> getPolygons() {
        return polygons;
    }

    public Set<LineSegment> getLineSegments() {
        return lineSegments;
    }

    public Set<Point> getControllerPoints() {
        return controllerPoints;
    }

    private void initListener() {
        addMouseMotionListener(editController);
        addMouseListener(editController);
    }

    public JToolBar getToolBar() {
        if (jToolBar == null) {
            jToolBar = new JToolBar();
            jToolBar.add(new CreateStraightLineButton(editController));
            jToolBar.add(new CreateCurveButton(editController));
            jToolBar.add(new CreatePathButton(editController));
        }
        return jToolBar;
    }

    @Override
    public void paint(Graphics g) {
        super.paint(g);

        drawPolygons(g);

        drawLineSegment(g);

        drawPoints(g);

        drawMessage(g);

        fpsManager.paintRunnable(90, this::repaint);
    }

    private void drawPoints(Graphics g) {
        for (Point controllerPoint : controllerPoints) {
            int x = controllerPoint.getXasInt();
            int y = controllerPoint.getYasInt();

            Graphics graphics = g.create(x - 4, y - 4, 8, 8);

            SwingUtil.antialiasingOn((Graphics2D) graphics);
            graphics.setColor(new Color(150, 150, 30));
            graphics.drawRect(1, 1, 6, 6);
        }
    }

    private void drawLineSegment(Graphics g) {
        for (LineSegment lineSegment : lineSegments) {
            Rectangle2D.Double round = lineSegment.round();
            int x = toInt(round.getX());
            int y = toInt(round.getY());

            Graphics graphics = g.create(x - 1, y - 1, toInt(round.getWidth() + 2), toInt(round.getHeight() + 2));

            try {
                LineSegment clone = lineSegment.clone();
                clone.move(1, 1);
                SwingUtil.antialiasingOn((Graphics2D) graphics);
                Point startPoint = clone.getStartPoint();
                Point endPoint = clone.getEndPoint();

                graphics.setColor(new Color(30, 150, 30));
                graphics.drawLine(startPoint.getXasInt(), startPoint.getYasInt(), endPoint.getXasInt(), endPoint.getYasInt());
            } finally {
                graphics.dispose();
            }
        }
    }

    private void drawPolygons(Graphics g) {
        for (Polygon polygon : polygons) {
            int x = toInt(polygon.getX());
            int y = toInt(polygon.getY());

            Graphics graphics = g.create(x - 1, y - 1, (int) Math.round(polygon.getWidth()) + 2, (int) Math.round(polygon.getHeight()) + 2);

            try {
                SwingUtil.antialiasingOn((Graphics2D) graphics);
                Polygon clone = polygon.clone();
                clone.move(1, 1);
                java.awt.Polygon awtPolygon = clone.toAwtPolygon();
                graphics.setColor(new Color(30, 150, 30));
                graphics.fillPolygon(awtPolygon);
                graphics.setColor(new Color(25, 25, 30, 100));
                graphics.drawPolygon(awtPolygon);
            } finally {
                graphics.dispose();
            }
        }
    }

    private void drawMessage(Graphics g) {
        if (message != null) {
            Graphics graphics = g.create(15, getHeight() - 30, 300, 30);
            try {
                SwingUtil.antialiasingOn((Graphics2D) graphics);
                graphics.drawString(message, 0, 15);
            } finally {
                graphics.dispose();
            }
        }
    }

    private static int toInt(double a) {
        return (int) Math.round(a);
    }

    public static void main(String[] args) {
        create();
    }

    public static Map create() {
        MapCreateView mapCreateView = new MapCreateView();
        mapCreateView.setLayout(new BorderLayout());
        mapCreateView.add(mapCreateView.getToolBar(), BorderLayout.NORTH);

        JFrame jFrame = new JFrame("MapCreateView");
        jFrame.setContentPane(mapCreateView);
        jFrame.setPreferredSize(new Dimension(900, 600));
        jFrame.pack();
        jFrame.setVisible(true);
        SwingUtil.toCenter(jFrame);

        return null;
    }

}
